#include <glib.h>
#include "generater.h"

Generater *
generater_create () {
    Generater *gen = g_new0 (Generater, 1);
    // TODO: insufficient memory
    gen->nodes = 0;
    gen->edges = g_array_new (FALSE, TRUE, sizeof (long int));
    gen->edge_attr = g_array_new (FALSE, TRUE, sizeof (EdgeAttr));
    gen->node_attr = g_array_new (FALSE, TRUE, sizeof (NodeAttr));
    gen->node_ptr2no = g_hash_table_new (g_direct_hash, g_direct_equal);
    gen->is_first_edge = TRUE;
    return gen;
}

/* NOTE: these func ref processor's memory*/
int
default_gen (void *opaque, ProvNode *from, ProvNode *to,  gchar *edge_type) {
    EdgeAttr ea;
    NodeAttr na;
    long int node_num;
    Generater *gen = opaque;
    // if (gen->is_first_edge) {
    //     na.host = from->host;
    //     na.name = from->name;
    //     na.type = from->type;
    //     g_array_append_val (gen->node_attr, na);
    //     g_hash_table_insert (gen->node_ptr2no, from, (gpointer)(gen->nodes));
    //     gen->nodes++;
    //     gen->is_first_edge = FALSE;
    // } else {
    //     na.host = from->host;
    //     na.name = from->name;
    //     na.type = from->type;
    //     g_array_append_val (gen->node_attr, na);
    //     g_hash_table_insert (gen->node_ptr2no, from, (gpointer)(gen->nodes));
    //     gen->nodes++;
    //     gen->is_first_edge = FALSE;
    // }

    if (!g_hash_table_contains (gen->node_ptr2no, from)) {
        g_hash_table_insert (gen->node_ptr2no, from, (gpointer)(gen->nodes));
        na.host = from->host;
        na.name = from->name;
        na.type  = from->type;
        g_array_append_val (gen->node_attr, na);
        gen->nodes++;
    }

    if (!g_hash_table_contains (gen->node_ptr2no, to)) {
        g_hash_table_insert (gen->node_ptr2no, to, (gpointer)(gen->nodes));
        na.host = to->host;
        na.name = to->name;
        na.type  = to->type;
        g_array_append_val (gen->node_attr, na);
        gen->nodes++;
    }

    ea.type = edge_type;
    // connect edges
    node_num = (long int)g_hash_table_lookup (gen->node_ptr2no, from);
    g_array_append_val (gen->edges, node_num);
    node_num = (long int)g_hash_table_lookup (gen->node_ptr2no, to);
    g_array_append_val (gen->edges, node_num);
    g_array_append_val (gen->edge_attr, ea);
    return 0;
} 

int generater_destructor (Generater* gen) {
    g_array_unref (gen->edges);
    g_array_unref (gen->node_attr);
    g_array_unref (gen->edge_attr);
    g_hash_table_unref (gen->node_ptr2no);
    g_free (gen);
    return 0;
}

int generater_reset (Generater *gen) {
    g_array_unref (gen->edges);
    g_array_unref (gen->node_attr);
    g_array_unref (gen->edge_attr);
    g_hash_table_unref (gen->node_ptr2no);
    gen->nodes = 0;
    gen->edges = g_array_new (FALSE, TRUE, sizeof (int));
    gen->edge_attr = g_array_new (FALSE, TRUE, sizeof (EdgeAttr));
    gen->node_attr = g_array_new (FALSE, TRUE, sizeof (NodeAttr));
    gen->node_ptr2no = g_hash_table_new (g_direct_hash, g_direct_equal);
    gen->is_first_edge = TRUE;
    return 0;
}

int
generater_draw (Generater *gen, FILE *out) {
    igraph_t g;
    igraph_vector_int_t edge_vec;
    guint iter;
    char name_buf[1024];
    igraph_vector_int_init_array (&edge_vec, (long int*)gen->edges->data, gen->edges->len);
    igraph_set_attribute_table(&igraph_cattribute_table);
    igraph_empty(&g, 0, IGRAPH_DIRECTED);
    igraph_add_vertices (&g, gen->nodes, NULL);
    igraph_add_edges (&g, &edge_vec, NULL);
    for (iter = 0; iter < gen->edge_attr->len; iter++) {
        SETEAS (&g, "label", iter,
                        ((g_array_index (gen->edge_attr, EdgeAttr, iter)).type));
    }
    for (iter = 0; iter < gen->node_attr->len; iter++) {
        g_snprintf (name_buf, sizeof (name_buf), "%s:%s", ((g_array_index (gen->node_attr, NodeAttr, iter)).name),
                        ((g_array_index (gen->node_attr, NodeAttr, iter)).host));
        SETVAS (&g, "label", iter, name_buf);
    }
    //igraph_write_graph_dot(&g, out);
    FILE* edgelist, *pajek, *graphml0, *graphml1;
    graphml0 = fopen ("./graph.graphml", "w+");
    graphml1 = fopen ("./graph.graphml", "w+");
    pajek = fopen ("./graph.pajek", "w+");
    edgelist = fopen ("./graph.edgelist", "w+");
    igraph_write_graph_graphml (&g, graphml0, TRUE);
    igraph_write_graph_graphml (&g, graphml1, FALSE);
    igraph_write_graph_pajek (&g, pajek);
    igraph_write_graph_edgelist (&g, edgelist);
    igraph_write_graph_dot (&g, out);
    igraph_destroy (&g);
    igraph_vector_int_destroy (&edge_vec);
    return 0;
}
